package com.franklin.ideaplugin.easytesting.controllerclient.request;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.franklin.ideaplugin.easytesting.common.utils.JsonUtils;
import com.franklin.ideaplugin.easytesting.controllerclient.beans.MethodData;
import com.franklin.ideaplugin.easytesting.controllerclient.beans.RequestData;
import com.franklin.ideaplugin.easytesting.controllerclient.utils.MyMapUtil;
import com.franklin.ideaplugin.easytesting.common.log.ILogger;
import com.franklin.ideaplugin.easytesting.common.log.LoggerFactory;
import lombok.RequiredArgsConstructor;
import okhttp3.*;
import org.springframework.http.HttpMethod;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
 * OkHttp实现
 *
 * @author Ye Junhui
 * @since 2023/6/29
 */
@RequiredArgsConstructor
public class OkHttpRequestInvoker implements IRequestInvoker {

    private final static ILogger log = LoggerFactory.getLogger(OkHttpRequestInvoker.class);

    private final OkHttpClient okHttpClient;

    private final static MediaType APPLICATION_JSON = MediaType.parse("application/json");
    private final static MediaType OCTET_STREAM = MediaType.parse("application/octet-stream");

    @Override
    public Object invoke(MethodData methodData, RequestData requestData, String targetUrl) {
        Request.Builder builder = new Request.Builder();
        UrlBuilder urlBuilder = new UrlBuilder(targetUrl);

        //url
        processUrl(requestData,urlBuilder);

        //请求头
        processHeader(requestData,builder);

        //请求体
        processBody(requestData,builder,urlBuilder);

        //构建请求
        Request request = builder.url(urlBuilder.build()).build();

        return doInvoke(request);
    }

    /**
     * 执行请求
     * @param request
     * @return
     */
    private Object doInvoke(Request request){
        log.info("执行http - {} 请求 url = {} ",request.method(),request.url().toString());
        Call call = okHttpClient.newCall(request);
        try {
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            if (Objects.nonNull(responseBody)){
                //字符串避免序列化
                return responseBody.string();
            }
        } catch (IOException e) {
            //ignore
        }
        return null;
    }

    /**
     * 处理url
     * @param requestData
     * @param urlBuilder
     */
    private void processUrl(RequestData requestData, UrlBuilder urlBuilder){
        String url = requestData.getUrl();
        if (StrUtil.isNotBlank(url)){
            for (Map.Entry<String, String> entry : requestData.getPathParams().entrySet()) {
                String target = "{" + entry.getKey() + "}";
                url = url.replace(target, entry.getValue());
            }
        }

        urlBuilder.addUrl(url);
    }

    /**
     * 处理请求体
     * @param requestData
     * @param requestBuilder
     * @param urlBuilder
     */
    private void processBody(RequestData requestData, Request.Builder requestBuilder, UrlBuilder urlBuilder) {
        Object requestBody = requestData.getRequestBody();
        //JSON请求体
        MultiValueMap<String, String> formData = requestData.getFormData();
        HttpMethod httpMethod = requestData.getHttpMethod();
        if (Objects.nonNull(requestBody)){
            //将所有form-data参数放入url
            MyMapUtil.forEachDeep(formData,urlBuilder::addQueryParams);

            //请求体
            RequestBody body = null;
            if (requestBody instanceof String){
                body = RequestBody.create(APPLICATION_JSON, (String) requestBody);
            }else {
                body = RequestBody.create(APPLICATION_JSON, JsonUtils.toJSONString(requestBody));
            }
            //设置body
            requestBuilder.method(httpMethod.name(),body);
        }else {
            if (httpMethod.equals(HttpMethod.GET) || httpMethod.equals(HttpMethod.DELETE)) {
                //将所有form-data参数放入url
                MyMapUtil.forEachDeep(formData,urlBuilder::addQueryParams);
            }else {
                MultipartBody.Builder formBuilder = new MultipartBody.Builder();
                formBuilder.setType(MultipartBody.FORM);
                //普通参数
                MyMapUtil.forEachDeep(formData,formBuilder::addFormDataPart);
                //文件
                MyMapUtil.forEachDeep(requestData.getFileData(),(k,v) -> {
                    File file = FileUtil.file(v);
                    if (Objects.isNull(file)){
                        return;
                    }
                    formBuilder.addFormDataPart(k,file.getName(),RequestBody.create(MultipartBody.FORM,file));
                });
                requestBuilder.method(httpMethod.name(), formBuilder.build());
            }
        }
    }

    /**
     * 处理请求头
     * @param requestData
     * @param builder
     */
    private void processHeader(RequestData requestData, Request.Builder builder) {
        MyMapUtil.forEachDeep(requestData.getHttpHeaders(), builder::addHeader);
    }

    private static class UrlBuilder{
        private StringBuilder basicBuilder;

        private boolean add = false;

        public UrlBuilder(String targetUrl) {
            this.basicBuilder = new StringBuilder(targetUrl);
        }

        public void addUrl(String url){
            basicBuilder.append(url);
        }

        public void addQueryParams(String k,String v){
            if (!add){
                basicBuilder.append('?');
                add = true;
            }else {
                basicBuilder.append('&');
            }
            basicBuilder.append(k).append('=').append(v);
        }

        public String build(){
            return basicBuilder.toString();
        }
    }
}
